package com.tbit.main.service.impl;

import com.google.common.cache.*;
import com.tbit.main.dao.core.AccountConfigDao;
import com.tbit.main.pojo.AccountConfig;
import com.tbit.main.service.GuavaCacheService;
import com.tbit.main.util.LogUtil;
import com.tbit.main.util.TimeUtil;
import org.springframework.context.ApplicationContext;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;


public class GuavaCacheServiceImpl implements GuavaCacheService {
    private static ApplicationContext applicationContext;
    public static final LoadingCache<Integer, AccountConfig> userCache
            //CacheBuilder的构造函数是私有的，只能通过其静态方法newBuilder()来获得CacheBuilder的实例
            = CacheBuilder.newBuilder()
            //设置并发级别为8，并发级别是指可以同时写缓存的线程数
            .concurrencyLevel(8)
            //设置写缓存后8秒钟过期
            .expireAfterWrite(8, TimeUnit.SECONDS)
            //设置写缓存后1秒钟刷新
            .refreshAfterWrite(1, TimeUnit.SECONDS)
            //设置缓存容器的初始容量为5
            .initialCapacity(5)
            //设置缓存最大容量为100，超过100之后就会按照LRU最近虽少使用算法来移除缓存项
            .maximumSize(100)
            //设置要统计缓存的命中率
            .recordStats()
            //设置缓存的移除通知
            .removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> notification) {
                    LogUtil.info(notification.getKey() + " 被移除了，原因： " + notification.getCause());
                }
            })
            //build方法中可以指定CacheLoader，在缓存不存在时通过CacheLoader的实现自动加载缓存
            .build(
                    new CacheLoader<Integer, AccountConfig>() {
                        @Override
                        public AccountConfig load(Integer key) {
                            LogUtil.info("缓存没有时，从数据库加载" + key);
                            AccountConfigDao accountConfigDao = (AccountConfigDao) applicationContext.getBean("accountConfigDao");
                            return accountConfigDao.getByAccountId(key);
                        }
                    }
            );


    @Override
    public <T, M> T get(Integer key, Function<M, T> function, M funcParam, Long expireTime, TimeUtil timeUtil) throws ExecutionException {
        T obj = null;

        if (key == null) {
            return null;
        }
        Cache<Integer, Object> ifPresent = (Cache<Integer, Object>) userCache.get(key);
        if (ifPresent == null && function != null) {
            obj = function.apply(funcParam);
            if (obj != null) {
                userCache.put(key, (AccountConfig) obj);
            }
        } else if (ifPresent != null) {
            obj = (T) ifPresent.getIfPresent(key);
        }
        return obj;
    }

}
